﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace SimpleRest.Tools.Scaffolding
{
    /// <summary>
    /// These are my comments
    /// </summary>
    public class ParsedTemplate
    {
        private ParsedTemplate(Type target)
        {
            // comments
            this.Target = target;
            this.InputParameters = new List<KeyValuePair<string, string>>();
        }

        public static MasterTemplate GenerateMasterTemplate(ObjectAssociationGraph<Type> graph)
        {
            List<ParsedTemplate> items = new List<ParsedTemplate>();

            foreach (Type type in graph.GetUniqueItems())
            {
                var rootParents = graph.GetParents(type);

                if (rootParents != null && rootParents.Count > 0)
                {
                    foreach (var parent in rootParents)
                    {
                        #region " Populate Uri Templates "

                        ParsedTemplate parsedTemplate = new ParsedTemplate(type);

                        string template = string.Empty;
                        Type tParent = parent;
                        List<Type> tParents = rootParents;
                        while (tParent != null)
                        {
                            template = template.Insert(0, "{" + tParent.Name.ToLower() + "Id}/");
                            template = template.Insert(0, tParent.Name + "/");
                            tParents = graph.GetParents(tParent);
                            if (tParents != null && tParents.Count > 0)
                                tParent = tParents[0];
                            else if (tParents != null && tParents.Count > 1)
                            {
                                string xxxx = string.Empty;
                            }
                            else
                                tParent = null;
                        }

                        // found a template
                        parsedTemplate.UriTemplate = template;

                        #endregion

                        #region " Find input parameters "

                        template = "";
                        tParents = rootParents;
                        tParent = parent;
                        while (tParent != null)
                        {
                            string idType = tParent.GetProperties().FirstOrDefault(d => d.Name.ToLower() == "id").PropertyType.FullName;
                            if (idType == null || (idType != typeof(string).FullName && idType != typeof(int).FullName))
                                idType = typeof(string).FullName;

                            parsedTemplate.InputParameters.Add(new KeyValuePair<string, string>(idType, tParent.Name.ToLower() + "Id"));
                            tParents = graph.GetParents(tParent);
                            if (tParents != null && tParents.Count > 0)
                                tParent = tParents[0];
                            else
                                tParent = null;
                        }

                        #endregion

                        items.Add(parsedTemplate);
                    }
                }
                else
                    items.Add(new ParsedTemplate(type) { UriTemplate = string.Empty });
            }

            return new MasterTemplate(items, graph);
        }

        public string UriTemplate { get; set; }
        public List<KeyValuePair<string, string>> InputParameters { get; set; }
        public Type Target { get; set; }

        public string GetFormattedInputParameters()
        {
            string template = string.Empty;

            if (InputParameters != null)
            {
                for (int i = 0; i < InputParameters.Count; i++)
                {
                    template = template.Insert(0, InputParameters[i].Key + " " + InputParameters[i].Value + ",");
                }
            }

            return template.TrimEnd(new char[] { ',' }).Replace(",", ", ");
        }

        public static List<string> GenerateUriTemplates(ObjectAssociationGraph<Type> graph, Type type)
        {
            List<string> templates = new List<string>();

            string template = "";

            if (type.FullName == typeof(D).FullName)
            {
                string xx1 = string.Empty;
            }

            var rootParents = graph.GetParents(type);

            if (rootParents != null)
                foreach (var parent in rootParents)
                {
                    template = string.Empty;
                    Type tParent = parent;
                    List<Type> tParents = rootParents;
                    while (tParent != null)
                    {
                        template = template.Insert(0, "{" + tParent.Name.ToLower() + "Id}/");
                        template = template.Insert(0, tParent.Name + "/");
                        tParents = graph.GetParents(tParent);
                        if (tParents != null && tParents.Count > 0)
                            tParent = tParents[0];
                        else if (tParents != null && tParents.Count > 1)
                        {
                            string xxxx = string.Empty;
                        }
                        else
                            tParent = null;
                    }

                    templates.Add(template);
                }

            return templates;
        }
    }

    public class MasterTemplate
    {
        public readonly List<ParsedTemplate> ParsedTemplates;
        public readonly ObjectAssociationGraph<Type> Graph;
        public readonly List<string> Primitives;

        public string GeneratedServiceInterfaceName { get; set; }
        public string GeneratedMockServiceName { get; set; }
        public string GeneratedFakeRepositoryServiceName { get; set; }
        public string TargetNamespace { get; set; }
        public bool GenerateIndividualEndpoints { get; set; }

        public MasterTemplate(IEnumerable<ParsedTemplate> parsedTemplates, ObjectAssociationGraph<Type> graph)
        {
            this.GeneratedServiceInterfaceName = "IGeneratedRestService";
            this.GeneratedMockServiceName = "ServiceWithoutRepository";
            this.GeneratedFakeRepositoryServiceName = "ServiceWithRepository";
            this.GenerateIndividualEndpoints = false;
            this.ParsedTemplates = parsedTemplates.ToList();
            var template = this.ParsedTemplates.FirstOrDefault();
            if (template != null)
                this.TargetNamespace = template.Target.Namespace;
            else
                this.TargetNamespace = "SomeNamespace";

            this.Graph = graph;

            this.Primitives = new List<string>()
            {
                typeof(string).FullName,
                typeof(Int16).FullName,
                typeof(Int32).FullName,
                typeof(Int64).FullName,
                typeof(UInt16).FullName,
                typeof(UInt32).FullName,
                typeof(UInt64).FullName,
                typeof(double).FullName,
                typeof(float).FullName,
                typeof(decimal).FullName,
                typeof(DateTime).FullName,
                typeof(Guid).FullName,
                typeof(char).FullName,
                typeof(bool).FullName
            };
            this.ParsedTemplates = ParsedTemplates.ToList();
        }

        private string GetIdType(Type type)
        {
            string item = typeof(string).FullName;

            foreach (PropertyInfo pinfo in type.GetProperties())
                if (pinfo.Name.ToLower().Equals("id"))
                {
                    item = pinfo.PropertyType.FullName;
                    break;
                }

            return item;
        }

        public List<CodeTemplate> GenerateMockRepositories()
        {
            List<CodeTemplate> items = new List<CodeTemplate>();

            foreach (var type in this.Graph.GetUniqueItems())
            {
                string idType = GetIdType(type);

                StringBuilder sb = new StringBuilder();
                sb.AppendLine("using System.Runtime.Serialization;");
                sb.AppendLine("using System.Linq;");
                sb.AppendLine("using System;");
                sb.AppendLine("using System.Net;");
                sb.AppendLine("using System.Collections.Generic;");
                sb.AppendLine("using System.ServiceModel;\n");

                sb.AppendLine("namespace " + this.TargetNamespace);
                sb.AppendLine("{");

                sb.AppendLine("\tpublic class " + type.Name + "FakeRepository : IRepository<" + type.FullName + ", " + idType + ">");
                sb.AppendLine("\t{");

                sb.AppendLine("\t\tprivate List<" + type.FullName + "> Items { get; set; }\n");
                sb.AppendLine("\t\tpublic " + type.Name + "FakeRepository()");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\tthis.Items = new List<" + type.FullName + ">();");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\tpublic " + type.FullName + " Get(" + idType + " id)");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\treturn this.Items.SingleOrDefault(d => d.Id == id);");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\tpublic List<" + type.FullName + "> GetAll()");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\treturn this.Items;");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\tpublic " + idType + " Create(" + type.FullName + " item)");
                sb.AppendLine("\t\t{");

                if (idType == typeof(string).FullName)
                    sb.AppendLine("\t\t\tstring id = Guid.NewGuid().ToString(\"N\");");
                else if (idType == typeof(int).FullName)
                    sb.AppendLine("\t\t\tint id = this.Items.Count + 1;");

                sb.AppendLine("\t\t\titem.Id = id;");
                sb.AppendLine("\t\t\tthis.Items.Add(item);");
                sb.AppendLine("\t\t\treturn id;");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\tpublic " + idType + " Create(" + idType + " id, " + type.FullName + " item)");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\titem.Id = id;");
                sb.AppendLine("\t\t\tthis.Items.Add(item);");
                sb.AppendLine("\t\t\treturn id;");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\tpublic bool Delete(" + idType + " id)");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\tvar target = this.Items.SingleOrDefault(d => d.Id == id);");
                sb.AppendLine("\t\t\tif (target != null)");
                sb.AppendLine("\t\t\t{");
                sb.AppendLine("\t\t\t\tthis.Items.Remove(target);");
                sb.AppendLine("\t\t\t\treturn true;");
                sb.AppendLine("\t\t\t}");
                sb.AppendLine("\t\t\telse return false;");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\tpublic bool Update(" + type.FullName + " item)");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\tvar target = this.Items.SingleOrDefault(d => d.Id == item.Id);");
                sb.AppendLine("\t\t\tif (target != null)");
                sb.AppendLine("\t\t\t{");
                sb.AppendLine("\t\t\t\ttarget = item;");
                sb.AppendLine("\t\t\t\treturn true;");
                sb.AppendLine("\t\t\t}");
                sb.AppendLine("\t\t\treturn false;");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\tpublic bool Update(" + idType + " id, " + type.FullName + " item)");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\tvar target = this.Items.SingleOrDefault(d => d.Id == id);");
                sb.AppendLine("\t\t\tif (target != null)");
                sb.AppendLine("\t\t\t{");
                sb.AppendLine("\t\t\t\ttarget = item;");
                sb.AppendLine("\t\t\t\ttarget.Id = id;");
                sb.AppendLine("\t\t\t\treturn true;");
                sb.AppendLine("\t\t\t}");
                sb.AppendLine("\t\t\treturn false;");

                sb.AppendLine("\t\t}");

                sb.AppendLine("\t}");
                sb.AppendLine("}");

                items.Add(new CodeTemplate() { CSharpTemplate = sb.ToString(), Name = type.Name + "FakeRepository" });
            }

            return items;
        }

        public CodeTemplate GenerateGenericRepositoryInterface()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("using System.Runtime.Serialization;");
            sb.AppendLine("using System.Collections.Generic;\n");

            sb.AppendLine("namespace " + this.TargetNamespace);
            sb.AppendLine("{");

            sb.AppendLine("\tpublic interface IRepository<T> : IRepository<T, string>");
            sb.AppendLine("\t{");

            sb.AppendLine("\t\t T Get(string id);");
            sb.AppendLine("\t\t List<T> GetAll();");
            sb.AppendLine("\t\t string Create(T item);");
            sb.AppendLine("\t\t string Create(string id, T item);");
            sb.AppendLine("\t\t bool Delete(string id);");
            sb.AppendLine("\t\t bool Update(T item);");
            sb.AppendLine("\t\t bool Update(string id, T item);");

            sb.AppendLine("\t}");
            sb.AppendLine("}");

            return new CodeTemplate() { CSharpTemplate = sb.ToString(), Name = "IRepository" };
        }

        public CodeTemplate GenerateGenericStringIdRepositoryInterface()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("using System.Runtime.Serialization;");
            sb.AppendLine("using System.Collections.Generic;\n");

            sb.AppendLine("namespace " + this.TargetNamespace);
            sb.AppendLine("{");

            sb.AppendLine("\tpublic interface IRepository<T> : IRepository<T, string>");
            sb.AppendLine("\t{");

            sb.AppendLine("\t\t T Get(string id);");
            sb.AppendLine("\t\t List<T> GetAll();");
            sb.AppendLine("\t\t string Create(T item);");
            sb.AppendLine("\t\t string Create(string id, T item);");
            sb.AppendLine("\t\t bool Delete(string id);");
            sb.AppendLine("\t\t bool Update(T item);");
            sb.AppendLine("\t\t bool Update(string id, T item);");

            sb.AppendLine("\t}");
            sb.AppendLine("}");

            return new CodeTemplate()
            {
                 CSharpTemplate = sb.ToString(),
                  Name = "IStringIdRepository"
            };
        }

        private string FormatIdString(string id)
        {
            string newId = string.Empty;
            for (int i = 0; i < id.Length; i++)
                if (i == 0)
                    newId += char.ToLower(id[i]).ToString();
                else
                    newId += id[i].ToString();

            return newId;
        }

        public List<CodeTemplate> GenerateDataContracts()
        {
            List<CodeTemplate> items = new List<CodeTemplate>();

            foreach (var type in this.Graph.GetUniqueItems())
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendLine("using System.Runtime.Serialization;");
                sb.AppendLine("using System.Collections.Generic;");

                sb.AppendLine("namespace " + this.TargetNamespace);
                sb.AppendLine("{");

                sb.AppendLine("\t[System.Xml.Serialization.XmlRoot(ElementName = \"" + type.Name + "\")]");
                sb.AppendLine("\t[DataContract(Name = \"" + type.Name + "\")]");
                sb.AppendLine("\tpublic class " + type.Name + "DataContract");
                sb.AppendLine("\t{");

                foreach (var t in this.Graph.GetParents(type))
                {
                    string idType = GetIdType(t);
                    sb.AppendLine("\t\t// The Parent Id for this object will be necessary on request Uri.");

                    sb.AppendLine("\t\t// Parent object type is of type " + t.Name + "DataContract.");
                    sb.AppendLine("\t\t// Parent Id type is of type " + idType + ".\n");
                }

                var uris = this.ParsedTemplates.Where(d => d.Target.FullName == type.FullName).Select(d => d.UriTemplate).ToList();

                for (int i = 0; i < uris.Count; i++)
                {
                    sb.AppendLine("\t\t// Uri to GET this object will be in the form of <root>/" + uris[i] + type.Name + "/{" + FormatIdString(type.Name + "Id") + "}.");
                    if (i != uris.Count - 1)
                        sb.AppendLine("\t\t// OR");
                    else
                        sb.AppendLine();
                }

                foreach (PropertyInfo p in type.GetProperties())
                {
                    if (!this.Primitives.Contains(p.PropertyType.FullName) && p.PropertyType.GetInterface(typeof(System.Collections.IEnumerable).FullName) != null)
                    {
                        string propName = "public List<";
                        var args = p.PropertyType.GetGenericArguments();

                        int primitiveCount = 0;
                        foreach (Type item in args)
                        {
                            if (this.Primitives.Contains(item.FullName))
                            {
                                propName += item.FullName + ",";
                                primitiveCount++;
                            }
                        }

                        if (primitiveCount > 1)
                        {
                            // WARNING
                            sb.AppendLine("\t\t//This is not possible, however multiple generic arguments were found on object.  Fix this so");
                            sb.AppendLine("\t\t//that your service data contract is not broken.");
                            sb.AppendLine("\t\t//[DataMember]");
                            sb.AppendLine("\t\t//" + propName.TrimEnd(new char[] { ',' }) + "> " + p.Name + " {get; set; }\n");
                        }
                        else if (primitiveCount == 1)
                        {
                            sb.AppendLine("\t\t[DataMember]");
                            sb.AppendLine("\t\t" + propName.TrimEnd(new char[] { ',' }) + "> " + p.Name + " {get; set; }\n");
                        }
                    }
                    else if (!this.Primitives.Contains(p.PropertyType.FullName))
                    {
                        var parent = this.Graph.GetParents(p.PropertyType).First();

                        sb.AppendLine("\t\t//" + p.PropertyType.Name + " is has a 1 to 1 association to this object.");
                        sb.AppendLine("\t\t[DataMember]");
                        sb.AppendLine("\t\tpublic " + GetIdType(p.PropertyType) + " " + p.PropertyType.Name + "Id {get; set; }\n");
                    }
                    else if (this.Primitives.Contains(p.PropertyType.FullName))
                    {
                        sb.AppendLine("\t\t[DataMember]");
                        sb.AppendLine("\t\tpublic " + p.PropertyType.FullName + " " + p.Name + " {get; set; }\n");
                    }
                }

                sb.AppendLine("\t}");
                sb.AppendLine("}");

                items.Add(new CodeTemplate() { CSharpTemplate = sb.ToString(), Name = type.Name + "DataContract" });
            }

            return items;
        }

        public CodeTemplate GenerateMockServiceUsingFakeRepositoryExtendingInterface()
        {
            CodeTemplate item = new CodeTemplate();

            StringBuilder sb = new StringBuilder();
            sb.AppendLine("using System.Runtime.Serialization;");
            sb.AppendLine("using System.Linq;");
            sb.AppendLine("using System.Net;");
            sb.AppendLine("using System;");
            sb.AppendLine("using System.Collections.Generic;");
            sb.AppendLine("using SimpleRest.Core;");
            sb.AppendLine("using System.ServiceModel;\n");

            sb.AppendLine("namespace " + this.TargetNamespace);
            sb.AppendLine("{");

            sb.AppendLine("\tpublic class " + this.GeneratedFakeRepositoryServiceName + " : " + this.GeneratedServiceInterfaceName);
            sb.AppendLine("\t{");

            int count = 0;
            List<string> ReposCreatedInCode = new List<string>();
            foreach (var parsedTemplate in this.ParsedTemplates)
            {
                Type type = parsedTemplate.Target;

                bool PossibleAmbiguity = false;

                if (this.ParsedTemplates.Count(d => d.Target.FullName == type.FullName) > 1)
                    PossibleAmbiguity = true;

                string idType = GetIdType(type);
                string variableName = (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "Repository";
                ReposCreatedInCode.Add(variableName);

                sb.AppendLine();
                sb.AppendLine("\t\t#region " + type.Name + " Service Methods");
                sb.AppendLine("\t\tprivate static " + type.Name + "FakeRepository " + variableName + " = new " + type.Name + "FakeRepository();");

                string input = parsedTemplate.GetFormattedInputParameters();

                sb.AppendLine("\t\tpublic OperationResponse<" + this.TargetNamespace + "." + type.Name + "DataContract> Get" + (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "(" + (string.IsNullOrEmpty(input) ? "" : (input + ", ")) + idType + " id)\n");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\tvar target = " + variableName + ".Get(id);");
                sb.AppendLine("\t\t\tif (target != null)");
                sb.AppendLine("\t\t\t\treturn new OperationResponse<" + this.TargetNamespace + "." + type.Name + "DataContract>(target.ToDataContract());");
                sb.AppendLine("\t\t\telse");
                sb.AppendLine("\t\t\t\treturn new OperationResponse<" + this.TargetNamespace + "." + type.Name + "DataContract>(null, HttpStatusCode.NotFound);");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\tpublic OperationResponse<" + idType + "> Create" + (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "([HttpBody]" + this.TargetNamespace + "." + type.Name + "DataContract request" + (string.IsNullOrEmpty(input) ? "" : (", " + input)) + ")\n");
                sb.AppendLine("\t\t{");

                sb.AppendLine("\t\t\t" + this.TargetNamespace + "." + type.Name + "DataContract item = request;");
                sb.AppendLine("\t\t\tvar id =" + variableName + ".Create(item.ToDomain());");
                sb.AppendLine("\t\t\treturn new OperationResponse<" + idType + ">(id);");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\tpublic OperationResponse Update" + (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "([HttpBody]" + this.TargetNamespace + "." + type.Name + "DataContract request, " + (string.IsNullOrEmpty(input) ? "" : (input + ", ")) + idType + " id)");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\tvar target = " + variableName + ".Get(id);");
                sb.AppendLine("\t\t\tif (target != null)");
                sb.AppendLine("\t\t\t{");
                sb.AppendLine("\t\t\t\ttarget = request.ToDomain();");
                sb.AppendLine("\t\t\t\treturn new OperationResponse();");
                sb.AppendLine("\t\t\t}");
                sb.AppendLine("\t\t\telse");
                sb.AppendLine("\t\t\t\treturn new OperationResponse(HttpStatusCode.NotFound);");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\tpublic OperationResponse Delete" + (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "(" + (string.IsNullOrEmpty(input) ? "" : (input + ", ")) + idType + " id)");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\tvar target = " + variableName + ".Get(id);");
                sb.AppendLine("\t\t\tif (target != null)");
                sb.AppendLine("\t\t\t{");
                sb.AppendLine("\t\t\t\t" + variableName + ".Delete(id);");
                sb.AppendLine("\t\t\t\treturn new OperationResponse();");
                sb.AppendLine("\t\t\t}");
                sb.AppendLine("\t\t\telse");
                sb.AppendLine("\t\t\t\treturn new OperationResponse(HttpStatusCode.NotFound);");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\tpublic OperationResponse<List<" + this.TargetNamespace + "." + type.Name + "DataContract>> GetAll" + (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "(" + (string.IsNullOrEmpty(input) ? "" : (input)) + ")");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\t\treturn new OperationResponse<List<" + this.TargetNamespace + "." + type.Name + "DataContract>>(" + variableName + ".GetAll().ToDataContract());");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\t#endregion");
                sb.AppendLine();

                if (PossibleAmbiguity)
                count++;
            }

            if (this.GenerateIndividualEndpoints)
            {
                foreach (var type in this.ParsedTemplates.Where(d => d.InputParameters.Count > 0).Select(d => d.Target).Distinct().ToList())
                {
                    string idType = GetIdType(type);
                    string variableName = type.Name + "Repository";

                    sb.AppendLine();
                    sb.AppendLine("\t\t#region Root Access " + type.Name + " Service Methods");

                    if (!ReposCreatedInCode.Contains(variableName))
                        sb.AppendLine("\t\tprivate static " + type.Name + "FakeRepository " + variableName + " = new " + type.Name + "FakeRepository();");

                    sb.AppendLine("\t\tpublic OperationResponse<" + this.TargetNamespace + "." + type.Name + "DataContract> GetRoot" + type.Name + "(" + idType + " id)\n");
                    sb.AppendLine("\t\t{");
                    sb.AppendLine("\t\t\tvar target = " + variableName + ".Get(id);");
                    sb.AppendLine("\t\t\tif (target != null)");
                    sb.AppendLine("\t\t\t\treturn new OperationResponse<" + this.TargetNamespace + "." + type.Name + "DataContract>(target.ToDataContract());");
                    sb.AppendLine("\t\t\telse");
                    sb.AppendLine("\t\t\t\treturn new OperationResponse<" + this.TargetNamespace + "." + type.Name + "DataContract>(null, HttpStatusCode.NotFound);");
                    sb.AppendLine("\t\t}");

                    sb.AppendLine("\t\tpublic OperationResponse UpdateRoot" + type.Name + "([HttpBody]" + this.TargetNamespace + "." + type.Name + "DataContract request, " + idType + " id)");
                    sb.AppendLine("\t\t{");
                    sb.AppendLine("\t\t\tvar target = " + variableName + ".Get(id);");
                    sb.AppendLine("\t\t\tif (target != null)");
                    sb.AppendLine("\t\t\t{");
                    sb.AppendLine("\t\t\t\ttarget = request.ToDomain();");
                    sb.AppendLine("\t\t\t\treturn new OperationResponse();");
                    sb.AppendLine("\t\t\t}");
                    sb.AppendLine("\t\t\telse");
                    sb.AppendLine("\t\t\t\treturn new OperationResponse(HttpStatusCode.NotFound);");
                    sb.AppendLine("\t\t}");

                    sb.AppendLine("\t\tpublic OperationResponse DeleteRoot" + type.Name + "(" + idType + " id)");
                    sb.AppendLine("\t\t{");
                    sb.AppendLine("\t\t\tvar target = " + variableName + ".Get(id);");
                    sb.AppendLine("\t\t\tif (target != null)");
                    sb.AppendLine("\t\t\t{");
                    sb.AppendLine("\t\t\t\t" + variableName + ".Delete(id);");
                    sb.AppendLine("\t\t\t\treturn new OperationResponse();");
                    sb.AppendLine("\t\t\t}");
                    sb.AppendLine("\t\t\telse");
                    sb.AppendLine("\t\t\t\treturn new OperationResponse(HttpStatusCode.NotFound);");
                    sb.AppendLine("\t\t}");

                    sb.AppendLine("\t\tpublic OperationResponse<List<" + this.TargetNamespace + "." + type.Name + "DataContract>> GetAllRoot" + type.Name + "()");
                    sb.AppendLine("\t\t{");
                    sb.AppendLine("\t\t\t\treturn new OperationResponse<List<" + this.TargetNamespace + "." + type.Name + "DataContract>>(" + variableName + ".GetAll().ToDataContract());");
                    sb.AppendLine("\t\t}");

                    sb.AppendLine("\t\t#endregion");
                    sb.AppendLine();
                }
            }

            sb.AppendLine("\t}");
            sb.AppendLine("}");

            return new CodeTemplate() { CSharpTemplate = sb.ToString(), Name = this.GeneratedFakeRepositoryServiceName };
        }

        public CodeTemplate GenerateMockServiceUsingFakeRepository()
        {
            CodeTemplate item = new CodeTemplate();

            StringBuilder sb = new StringBuilder();
            sb.AppendLine("using System.Runtime.Serialization;");
            sb.AppendLine("using System.Linq;");
            sb.AppendLine("using System.Net;");
            sb.AppendLine("using System;");
            sb.AppendLine("using System.Collections.Generic;");
            sb.AppendLine("using SimpleRest.Core;");
            sb.AppendLine("using System.ServiceModel;\n");

            sb.AppendLine("namespace " + this.TargetNamespace);
            sb.AppendLine("{");

            sb.AppendLine("\tpublic class " + this.GeneratedFakeRepositoryServiceName);
            sb.AppendLine("\t{");

            int count = 0;
            List<string> ReposCreatedInCode = new List<string>();
            foreach (var parsedTemplate in this.ParsedTemplates)
            {
                Type type = parsedTemplate.Target;

                bool PossibleAmbiguity = false;

                if (this.ParsedTemplates.Count(d => d.Target.FullName == type.FullName) > 1)
                    PossibleAmbiguity = true;

                string idType = GetIdType(type);
                string variableName = (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "Repository";

                string uriTemplate = parsedTemplate.UriTemplate;
                string input = parsedTemplate.GetFormattedInputParameters();
                ReposCreatedInCode.Add(variableName);

                sb.AppendLine();
                sb.AppendLine("\t\t#region " + type.Name + " Service Methods");
                sb.AppendLine("\t\tprivate static " + type.Name + "FakeRepository " + variableName + " = new " + type.Name + "FakeRepository();");

                sb.AppendLine("\t\t[WebGet(UriTemplate = \"" + uriTemplate + type.Name + "/{id}" + "\")]");
                sb.AppendLine("\t\tpublic OperationResponse<" + this.TargetNamespace + "." + type.Name + "DataContract> Get" + (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "(" + (string.IsNullOrEmpty(input) ? "" : (input + ", ")) + idType + " id)\n");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\tvar target = " + variableName + ".Get(id);");
                sb.AppendLine("\t\t\tif (target != null)");
                sb.AppendLine("\t\t\t\treturn new OperationResponse<" + this.TargetNamespace + "." + type.Name + "DataContract>(target.ToDataContract());");
                sb.AppendLine("\t\t\telse");
                sb.AppendLine("\t\t\t\treturn new OperationResponse<" + this.TargetNamespace + "." + type.Name + "DataContract>(null, HttpStatusCode.NotFound);");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\t[WebInvoke(Method = \"POST\", UriTemplate = \"" + uriTemplate + type.Name + "\")]");
                sb.AppendLine("\t\tpublic OperationResponse<" + idType + "> Create" + (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "([HttpBody]" + this.TargetNamespace + "." + type.Name + "DataContract request" + (string.IsNullOrEmpty(input) ? "" : (", " + input)) + ")\n");
                sb.AppendLine("\t\t{");

                sb.AppendLine("\t\t\t" + this.TargetNamespace + "." + type.Name + "DataContract item = request;");
                sb.AppendLine("\t\t\tvar id =" + variableName + ".Create(item.ToDomain());");
                sb.AppendLine("\t\t\treturn new OperationResponse<" + idType + ">(id);");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\t[WebInvoke(Method = \"PUT\", UriTemplate = \"" + uriTemplate + type.Name + "/{id}" + "\")]");
                sb.AppendLine("\t\tpublic OperationResponse Update" + (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "([HttpBody]" + this.TargetNamespace + "." + type.Name + "DataContract request, " + (string.IsNullOrEmpty(input) ? "" : (input + ", ")) + idType + " id)");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\tvar target = " + variableName + ".Get(id);");
                sb.AppendLine("\t\t\tif (target != null)");
                sb.AppendLine("\t\t\t{");
                sb.AppendLine("\t\t\t\ttarget = request.ToDomain();");
                sb.AppendLine("\t\t\t\treturn new OperationResponse();");
                sb.AppendLine("\t\t\t}");
                sb.AppendLine("\t\t\telse");
                sb.AppendLine("\t\t\t\treturn new OperationResponse(HttpStatusCode.NotFound);");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\t[WebInvoke(Method = \"DELETE\", UriTemplate = \"" + (uriTemplate + type.Name + "/{id}") + "\")]");
                sb.AppendLine("\t\tpublic OperationResponse Delete" + (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "(" + (string.IsNullOrEmpty(input) ? "" : (input + ", ")) + idType + " id)");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\tvar target = " + variableName + ".Get(id);");
                sb.AppendLine("\t\t\tif (target != null)");
                sb.AppendLine("\t\t\t{");
                sb.AppendLine("\t\t\t\t" + variableName + ".Delete(id);");
                sb.AppendLine("\t\t\t\treturn new OperationResponse();");
                sb.AppendLine("\t\t\t}");
                sb.AppendLine("\t\t\telse");
                sb.AppendLine("\t\t\t\treturn new OperationResponse(HttpStatusCode.NotFound);");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\t[WebGet(UriTemplate = \"" + (string.IsNullOrEmpty(uriTemplate) ? "" : uriTemplate) + type.Name + "\")]");
                sb.AppendLine("\t\tpublic OperationResponse<List<" + this.TargetNamespace + "." + type.Name + "DataContract>> GetAll" + (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "(" + (string.IsNullOrEmpty(input) ? "" : (input)) + ")");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\t\treturn new OperationResponse<List<" + this.TargetNamespace + "." + type.Name + "DataContract>>(" + variableName + ".GetAll().ToDataContract());");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\t#endregion");
                sb.AppendLine();

                if (PossibleAmbiguity)
                    count++;
            }

            sb.AppendLine("\t}");
            sb.AppendLine("}");

            return new CodeTemplate() { CSharpTemplate = sb.ToString(), Name = this.GeneratedFakeRepositoryServiceName };
        }

        public CodeTemplate GenerateServiceContract()
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("using System.Runtime.Serialization;");
            sb.AppendLine("using System.ServiceModel;");
            sb.AppendLine("using SimpleRest.Core.Routing;");
            sb.AppendLine("using System.Collections.Generic;");
            sb.AppendLine("using SimpleRest.Core;\n");

            sb.AppendLine("namespace " + this.TargetNamespace);
            sb.AppendLine("{");

            sb.AppendLine("\tpublic interface " + this.GeneratedServiceInterfaceName);
            sb.AppendLine("\t{");

            int count = 0;
            foreach (var parsedTemplate in this.ParsedTemplates)
            {
                Type type = parsedTemplate.Target;
                bool PossibleAmbiguity = false;

                if (this.ParsedTemplates.Count(d => d.Target.FullName == type.FullName) > 1)
                    PossibleAmbiguity = true;
                
                string idType = GetIdType(type);

                string uriTemplate = parsedTemplate.UriTemplate;
                string input = parsedTemplate.GetFormattedInputParameters();

                sb.AppendLine("\t\t#region " + type.Name + " Interface Methods");
                sb.AppendLine("\t\t[WebGet(UriTemplate = \"" + uriTemplate + type.Name + "/{id}" + "\")]");
                sb.AppendLine("\t\tOperationResponse<" + this.TargetNamespace + "." + type.Name + "DataContract> Get" + (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "(" + (string.IsNullOrEmpty(input) ? "" : (input + ", ")) + "" + idType + " id);\n");


                sb.AppendLine("\t\t[WebInvoke(Method = \"POST\", UriTemplate = \"" + uriTemplate + type.Name + "\")]");
                sb.AppendLine("\t\tOperationResponse<" + idType + "> Create" + (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "([HttpBody]" + this.TargetNamespace + "." + type.Name + "DataContract request" + (string.IsNullOrEmpty(input) ? "" : (", " + input)) + ");\n");


                sb.AppendLine("\t\t[WebInvoke(Method = \"PUT\", UriTemplate = \"" + uriTemplate + type.Name + "/{id}" + "\")]");
                sb.AppendLine("\t\tOperationResponse Update" + (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "([HttpBody]" + this.TargetNamespace + "." + type.Name + "DataContract request, " + (string.IsNullOrEmpty(input) ? "" : (input + ", ")) + "" + idType + " id);\n");


                sb.AppendLine("\t\t[WebInvoke(Method = \"DELETE\", UriTemplate = \"" + (uriTemplate + type.Name + "/{id}") + "\")]");
                sb.AppendLine("\t\tOperationResponse Delete" + (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "(" + (string.IsNullOrEmpty(input) ? "" : (input + ", ")) + idType + " id);\n");

                sb.AppendLine("\t\t[WebGet(UriTemplate = \"" + (string.IsNullOrEmpty(uriTemplate) ? "" : uriTemplate) + type.Name + "\")]");
                sb.AppendLine("\t\tOperationResponse<List<" + type.Name + "DataContract>> GetAll" + (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "(" + (string.IsNullOrEmpty(input) ? "" : (input)) + ");\n");

                sb.AppendLine("\t\t#endregion");

                if (PossibleAmbiguity)
                    count++;
            }

            if (this.GenerateIndividualEndpoints)
            {
                foreach (var type in this.ParsedTemplates.Where(d => d.InputParameters.Count > 0).Select(d => d.Target).Distinct().ToList())
                {
                   // bool PossibleAmbiguity = false;
                 
                    //if (this.ParsedTemplates.Count(d => d.Target.FullName == type.FullName) > 1)
                    //    PossibleAmbiguity = true;

                    string idType = GetIdType(type);

                    sb.AppendLine("\t\t#region Root Access " + type.Name + " Interface Methods");
                    sb.AppendLine("\t\t[WebGet(UriTemplate = \"" + type.Name + "/{id}" + "\")]");
                    sb.AppendLine("\t\tOperationResponse<" + this.TargetNamespace + "." + type.Name + "DataContract> GetRoot" + type.Name + "(" + idType + " id);\n");


                    sb.AppendLine("\t\t[WebInvoke(Method = \"PUT\", UriTemplate = \"" + type.Name + "/{id}" + "\")]");
                    sb.AppendLine("\t\tOperationResponse UpdateRoot" + type.Name + "([HttpBody]" + this.TargetNamespace + "." + type.Name + "DataContract request," + idType + " id);\n");


                    sb.AppendLine("\t\t[WebInvoke(Method = \"DELETE\", UriTemplate = \"" + type.Name + "/{id}" + "\")]");
                    sb.AppendLine("\t\tOperationResponse DeleteRoot" + type.Name + "(" + idType + " id);\n");

                    sb.AppendLine("\t\t[WebGet(UriTemplate = \"" + type.Name + "\")]");
                    sb.AppendLine("\t\tOperationResponse<List<" + type.Name + "DataContract>> GetAllRoot" + type.Name + "();\n");

                    sb.AppendLine("\t\t#endregion");

                   // if (PossibleAmbiguity)
                   //     count++;
                }
            }

            sb.AppendLine("\t}");
            sb.AppendLine("}");

            return new CodeTemplate() { CSharpTemplate = sb.ToString(), Name = this.GeneratedServiceInterfaceName };
        }

        public CodeTemplate GenerateSingleMockService()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("using System.Runtime.Serialization;");
            sb.AppendLine("using System.Linq;");
            sb.AppendLine("using System.Net;");
            sb.AppendLine("using System;");
            sb.AppendLine("using System.Collections.Generic;");
            sb.AppendLine("using SimpleRest.Core;");
            sb.AppendLine("using System.ServiceModel;\n");

            sb.AppendLine("namespace " + this.TargetNamespace);
            sb.AppendLine("{");

            sb.AppendLine("\tpublic class " + this.GeneratedMockServiceName + " : " + this.GeneratedServiceInterfaceName);
            sb.AppendLine("\t{");

            int count = 0;
            List<string> ReposCreatedInCode = new List<string>();
            foreach (var parsedTemplate in this.ParsedTemplates)
            {
                Type type = parsedTemplate.Target;
                bool PossibleAmbiguity = false;

                if (this.ParsedTemplates.Count(d => d.Target.FullName == type.FullName) > 1)
                    PossibleAmbiguity = true;

                string idType = GetIdType(type);
                string variableName = (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "Items";
                ReposCreatedInCode.Add(variableName);
                sb.AppendLine();
                sb.AppendLine("\t\t#region " + type.Name + " Service Methods");
                sb.AppendLine("\t\tprivate static List<" + this.TargetNamespace + "." + type.Name + "DataContract> " + variableName + " = new List<" + this.TargetNamespace + "." + type.Name + "DataContract>();\n");

                string input = parsedTemplate.GetFormattedInputParameters();

                sb.AppendLine("\t\tpublic OperationResponse<" + this.TargetNamespace + "." + type.Name + "DataContract> Get" + (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "(" + (string.IsNullOrEmpty(input) ? "" : (input + ", ")) + idType + " id)\n");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\tvar target = " + variableName + ".SingleOrDefault(d => d.Id == id);");
                sb.AppendLine("\t\t\tif (target != null)");
                sb.AppendLine("\t\t\t\treturn new OperationResponse<" + this.TargetNamespace + "." + type.Name + "DataContract>(target);");
                sb.AppendLine("\t\t\telse");
                sb.AppendLine("\t\t\t\treturn new OperationResponse<" + this.TargetNamespace + "." + type.Name + "DataContract>(null, HttpStatusCode.NotFound);");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\tpublic OperationResponse<" + idType + "> Create" + (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "([HttpBody]" + this.TargetNamespace + "." + type.Name + "DataContract request" + (string.IsNullOrEmpty(input) ? "" : (", " + input)) + ")\n");
                sb.AppendLine("\t\t{");

                if (idType == typeof(string).FullName)
                    sb.AppendLine("\t\t\tstring id = Guid.NewGuid().ToString(\"N\");");
                else if (idType == typeof(Int32).FullName)
                    sb.AppendLine("\t\t\tint id = " + variableName + ".Count + 1;");

                sb.AppendLine("\t\t\t" + this.TargetNamespace + "." + type.Name + "DataContract item = request;");
                sb.AppendLine("\t\t\titem.Id = id;");
                sb.AppendLine("\t\t\t" + variableName + ".Add(item);");
                sb.AppendLine("\t\t\treturn new OperationResponse<" + idType + ">(id);");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\tpublic OperationResponse Update" + (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "([HttpBody]" + this.TargetNamespace + "." + type.Name + "DataContract request, " + (string.IsNullOrEmpty(input) ? "" : (input + ", ")) + idType + " id)");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\tvar target = " + variableName + ".SingleOrDefault(d => d.Id == id);");
                sb.AppendLine("\t\t\tif (target != null)");
                sb.AppendLine("\t\t\t{");
                sb.AppendLine("\t\t\t\ttarget = request;");
                sb.AppendLine("\t\t\t\ttarget.Id = id;");
                sb.AppendLine("\t\t\t\treturn new OperationResponse();");
                sb.AppendLine("\t\t\t}");
                sb.AppendLine("\t\t\telse");
                sb.AppendLine("\t\t\t\treturn new OperationResponse(HttpStatusCode.NotFound);");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\tpublic OperationResponse Delete" + (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "(" + (string.IsNullOrEmpty(input) ? "" : (input + ", ")) + idType + " id)");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\tvar target = " + variableName + ".SingleOrDefault(d => d.Id == id);");
                sb.AppendLine("\t\t\tif (target != null)");
                sb.AppendLine("\t\t\t{");
                sb.AppendLine("\t\t\t\t" + variableName + ".Remove(target);");
                sb.AppendLine("\t\t\t\treturn new OperationResponse();");
                sb.AppendLine("\t\t\t}");
                sb.AppendLine("\t\t\telse");
                sb.AppendLine("\t\t\t\treturn new OperationResponse(HttpStatusCode.NotFound);");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\tpublic OperationResponse<List<" + this.TargetNamespace + "." + type.Name + "DataContract>> GetAll" + (PossibleAmbiguity ? type.Name + count.ToString() : type.Name) + "(" + (string.IsNullOrEmpty(input) ? "" : (input)) + ")");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\t\treturn new OperationResponse<List<" + this.TargetNamespace + "." + type.Name + "DataContract>>(" + variableName + ");");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\t#endregion");
                sb.AppendLine();

                if (PossibleAmbiguity)
                count++;
            }

            if (this.GenerateIndividualEndpoints)
            {
                foreach (var type in this.ParsedTemplates.Where(d => d.InputParameters.Count > 0).Select(d => d.Target).Distinct().ToList())
                {
                    string idType = GetIdType(type);
                    string variableName = type.Name + "Items";

                    sb.AppendLine("\t\t#region Root Access " + type.Name + " Service Methods");

                    if (!ReposCreatedInCode.Contains(variableName))
                        sb.AppendLine("\t\tprivate static List<" + this.TargetNamespace + "." + type.Name + "DataContract> " + variableName + " = new List<" + this.TargetNamespace + "." + type.Name + "DataContract>();\n");

                    sb.AppendLine("\t\tpublic OperationResponse<" + this.TargetNamespace + "." + type.Name + "DataContract> GetRoot" + type.Name + "(" + idType + " id)\n");
                    sb.AppendLine("\t\t{");
                    sb.AppendLine("\t\t\tvar target = " + variableName + ".SingleOrDefault(d => d.Id == id);");
                    sb.AppendLine("\t\t\tif (target != null)");
                    sb.AppendLine("\t\t\t\treturn new OperationResponse<" + this.TargetNamespace + "." + type.Name + "DataContract>(target);");
                    sb.AppendLine("\t\t\telse");
                    sb.AppendLine("\t\t\t\treturn new OperationResponse<" + this.TargetNamespace + "." + type.Name + "DataContract>(null, HttpStatusCode.NotFound);");
                    sb.AppendLine("\t\t}");

                    sb.AppendLine("\t\tpublic OperationResponse UpdateRoot" + type.Name + "([HttpBody]" + this.TargetNamespace + "." + type.Name + "DataContract request, " + idType + " id)");
                    sb.AppendLine("\t\t{");
                    sb.AppendLine("\t\t\tvar target = " + variableName + ".SingleOrDefault(d => d.Id == id);");
                    sb.AppendLine("\t\t\tif (target != null)");
                    sb.AppendLine("\t\t\t{");
                    sb.AppendLine("\t\t\t\ttarget = request;");
                    sb.AppendLine("\t\t\t\ttarget.Id = id;");
                    sb.AppendLine("\t\t\t\treturn new OperationResponse();");
                    sb.AppendLine("\t\t\t}");
                    sb.AppendLine("\t\t\telse");
                    sb.AppendLine("\t\t\t\treturn new OperationResponse(HttpStatusCode.NotFound);");
                    sb.AppendLine("\t\t}");

                    sb.AppendLine("\t\tpublic OperationResponse DeleteRoot" + type.Name + "(" + idType + " id)");
                    sb.AppendLine("\t\t{");
                    sb.AppendLine("\t\t\tvar target = " + variableName + ".SingleOrDefault(d => d.Id == id);");
                    sb.AppendLine("\t\t\tif (target != null)");
                    sb.AppendLine("\t\t\t{");
                    sb.AppendLine("\t\t\t\t" + variableName + ".Remove(target);");
                    sb.AppendLine("\t\t\t\treturn new OperationResponse();");
                    sb.AppendLine("\t\t\t}");
                    sb.AppendLine("\t\t\telse");
                    sb.AppendLine("\t\t\t\treturn new OperationResponse(HttpStatusCode.NotFound);");
                    sb.AppendLine("\t\t}");

                    sb.AppendLine("\t\tpublic OperationResponse<List<" + this.TargetNamespace + "." + type.Name + "DataContract>> GetAllRoot" + type.Name + "()");
                    sb.AppendLine("\t\t{");
                    sb.AppendLine("\t\t\t\treturn new OperationResponse<List<" + this.TargetNamespace + "." + type.Name + "DataContract>>(" + variableName + ");");
                    sb.AppendLine("\t\t}");

                    sb.AppendLine("\t\t#endregion");
                }
            }

            sb.AppendLine("\t}");
            sb.AppendLine("}");

            return new CodeTemplate() { CSharpTemplate = sb.ToString(), Name = this.GeneratedMockServiceName };
        }

        public CodeTemplate GenerateMappings()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("using System;");
            sb.AppendLine("using System.Linq;");
            sb.AppendLine("using AutoMapper;");
            sb.AppendLine("using System.Collections.Generic;\n");

            sb.AppendLine("namespace " + this.TargetNamespace);
            sb.AppendLine("{");

            sb.AppendLine("\tpublic static class Mappings");
            sb.AppendLine("\t{");

            sb.AppendLine("\t\tstatic Mappings()");
            sb.AppendLine("\t\t{");

            foreach (Type t in this.Graph.GetUniqueItems())
            {
                StringBuilder toContractBuilder = new StringBuilder("");
                StringBuilder toDomainBuilder = new StringBuilder("");

                foreach (PropertyInfo pinfo in t.GetProperties())
                {
                    if (this.Primitives.Contains(pinfo.PropertyType.FullName))
                    {
                        // this is fine, will map natively
                    }
                    else
                    {
                        if (pinfo.PropertyType.GetInterface(typeof(System.Collections.IEnumerable).FullName) == null)
                        {
                            // this will not map natively.
                            toContractBuilder.Append("\t\t\t.ForMember(d => d." + pinfo.Name + ", d => d.Ignore())\n");

                            toDomainBuilder.Append("\t\t\t.ForMember(d => d." + pinfo.Name + "Id, d => d.ResolveUsing(a => a.Id))\n"); // should be 1 to 1 with id associated
                        }
                        else
                        {
                            var args = pinfo.PropertyType.GetGenericArguments();
                            if (args.Any(d => !this.Primitives.Contains(d.FullName)))
                            {
                                // will not map.  Does not exist on data contract
                                toContractBuilder.Append("\t\t\t\t.ForMember(d => d." + pinfo.Name + ", d => d.Ignore())\n");
                            }
                            else
                            {
                                // containing generic list contains primitives and should map natively
                            }
                        }
                    }
                }

                var props = toContractBuilder.ToString();
                if (props.Length > 1)
                {
                    sb.AppendLine("\t\t\tMapper.CreateMap<" + t.Name + "DataContract, " + t.FullName + ">()\n" + props.TrimEnd(new char[] { '\n' }) + ";");
                }
                else
                    sb.AppendLine("\t\t\tMapper.CreateMap<" + t.Name + "DataContract, " + t.FullName + ">();");
                sb.AppendLine();

                props = toDomainBuilder.ToString();
                if (props.Length > 1)
                {
                    sb.AppendLine("\t\t\tMapper.CreateMap<" + t.FullName + ", " + t.Name + "DataContract>()\n" + props.TrimEnd(new char[] { '\n' }) + ";");
                }
                else
                    sb.AppendLine("\t\t\tMapper.CreateMap<" + t.FullName + ", " + t.Name + "DataContract>();");
                sb.AppendLine();

            }
            sb.AppendLine("\t\t}");

            foreach (var type in this.Graph.GetUniqueItems())
            {
                sb.AppendLine("\t\t#region Mapping for " + type.Name);
                sb.AppendLine("\t\tpublic static " + type.Name + "DataContract ToDataContract(this " + type.FullName + " item)");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\treturn Mapper.Map<" + type.FullName + ", " + type.Name + "DataContract>(item);");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\tpublic static List<" + type.Name + "DataContract> ToDataContract(this List<" + type.FullName + "> items)");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\tList<" + type.Name + "DataContract> newItems = new List<" + type.Name + "DataContract>();");
                sb.AppendLine("\t\t\tforeach (var item in items)");
                sb.AppendLine("\t\t\t{");
                sb.AppendLine("\t\t\t\tnewItems.Add(item.ToDataContract());");
                sb.AppendLine("\t\t\t}");
                sb.AppendLine("\t\t\treturn newItems;");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\tpublic static List<" + type.FullName + "> ToDomain(this List<" + type.Name + "DataContract> items)");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\tList<" + type.FullName + "> newItems = new List<" + type.FullName + ">();");
                sb.AppendLine("\t\t\tforeach (var item in items)");
                sb.AppendLine("\t\t\t{");
                sb.AppendLine("\t\t\t\tnewItems.Add(item.ToDomain());");
                sb.AppendLine("\t\t\t}");
                sb.AppendLine("\t\t\treturn newItems;");
                sb.AppendLine("\t\t}");

                sb.AppendLine("\t\t///<summary>");
                sb.AppendLine("\t\t/// This extension method will only populate domain items that are directly on the data contract being specified.");
                sb.AppendLine("\t\t/// If there are any complex types or lists of complex types on the domain object it will not be returned from this extension method.");
                sb.AppendLine("\t\t///</summary>");
                sb.AppendLine("\t\t/// <returns>List of semi-saturated " + type.FullName + " objects</returns>");
                sb.AppendLine("\t\tpublic static " + type.FullName + " ToDomain(this " + type.Name + "DataContract item)");
                sb.AppendLine("\t\t{");
                sb.AppendLine("\t\t\treturn Mapper.Map<" + type.Name + "DataContract, " + type.FullName + ">(item);");
                sb.AppendLine("\t\t}");

                List<Type> complexTypes = GetComplexTypes(type);
                int complexSenariosFound = 0;

                if (complexTypes != null && complexTypes.Count > 0)
                {
                    sb.Append("\t\tpublic static " + type.FullName + " ToDomain(this " + type.Name + "DataContract item");

                    if (complexTypes != null)
                        foreach (var t in complexTypes)
                        {
                            if (t.GetInterface(typeof(System.Collections.IEnumerable).FullName) != null)
                            {
                                var args = t.GetGenericArguments();
                                var arg = args[0];

                                if (args.Length > 1)
                                {
                                    complexSenariosFound += 1;

                                    sb.Append(", " + t.GetGenericTypeDefinition().FullName.Split('`')[0] + "<");
                                    for (int i = 0; i < args.Length; i++)
                                    {
                                        if (i == 0)
                                            sb.Append(args[i].FullName);
                                        else
                                            sb.Append(", " + args[i].FullName);
                                    }
                                    sb.Append("> additionalItems" + complexSenariosFound.ToString());
                                }
                                else if (!this.Primitives.Contains(arg.FullName))
                                    sb.Append(", List<" + arg.FullName + "> " + (arg.Name.EndsWith("s") ? arg.Name : arg.Name + "s"));
                            }
                            else
                            {
                                sb.Append(", " + t.FullName + " " + t.Name);
                            }
                        }


                    sb.AppendLine(")");

                    sb.AppendLine("\t\t{");

                    sb.AppendLine("\t\t\tvar newItem = Mapper.Map<" + type.Name + "DataContract, " + type.FullName + ">(item);");

                    var properties = type.GetProperties();

                    complexSenariosFound = 0;
                    if (complexTypes != null)
                        foreach (var t in complexTypes)
                        {
                            if (t.GetInterface(typeof(System.Collections.IEnumerable).FullName) != null)
                            {
                                var args = t.GetGenericArguments();
                                var arg = args[0];

                                if (args.Length > 1)
                                {
                                    complexSenariosFound += 1;

                                    sb.AppendLine("\t\t\tnewItem." + properties.First(d => d.PropertyType.FullName == t.FullName).Name + " = additionalItems" + complexSenariosFound.ToString() + ";");
                                }
                                else if (!this.Primitives.Contains(arg.FullName))
                                {
                                    sb.AppendLine("\t\t\tnewItem." + properties.First(d => d.PropertyType.FullName == t.FullName).Name + " = " + (arg.Name.EndsWith("s") ? arg.Name : arg.Name + "s;"));
                                }
                            }
                            else
                            {
                                sb.AppendLine("\t\t\tnewItem." + properties.First(d => d.PropertyType.FullName == t.FullName).Name + " = " + t.Name + ";");
                            }
                        }

                    sb.AppendLine("\t\t\treturn newItem;");
                    sb.AppendLine("\t\t}");
                }

                sb.AppendLine("\t\t#endregion");
                sb.AppendLine();
            }

            sb.AppendLine("\t}");
            sb.AppendLine("}");

            return new CodeTemplate()
            {
                 CSharpTemplate = sb.ToString(),
                  Name = "Mappings"
            };
        }

        private List<Type> GetComplexTypes(Type type)
        {
            List<Type> items = new List<Type>();

            foreach (var pinfo in type.GetProperties())
            {
                if (!this.Primitives.Contains(pinfo.PropertyType.FullName))
                    items.Add(pinfo.PropertyType);
            }

            return items;
        }
    }

    public class CodeTemplate
    {
        public string CSharpTemplate { get; set; }
        public string Name { get; set; }
    }
}
